#include "stdafx.h"
#include "JsScript.h"
#include <assert.h>
#include "Cpu.h"
#include "Server.h"
#include <future>


JsScriptManager::JsScriptManager()
    :scripts(std::unordered_map<uint64_t, std::promise<QVariant>>(5120))
{
    assert(_instance == nullptr);
    _instance = this;
}


JsScriptManager::~JsScriptManager()
{
}

void JsScriptManager::init(Cpu *cpu)
{
    assert(_instance == nullptr);
    _instance = new JsScriptManager;

    cpu->setOn("scriptFinished", _instance, &JsScriptManager::onJsScriptFinished);
}

//----------------------------------------------------------------
// Public static scirpt runners
//----------------------------------------------------------------
std::future<QVariant> JsScriptManager::runOnAddon(const QString &script)
{
    return _instance->_runOnAddon(script);
}

std::future<QVariant> JsScriptManager::runFunctionOnAddon(const QString &name, const QVariantList &args)
{
    return _instance->_runFunctionOnAddon(name, args);
}

std::future<QVariant> JsScriptManager::runOnTab(const QString &tab, const QString &script)
{
    return _instance->_runOnTab(tab, script);
}

std::future<QVariant> JsScriptManager::runFunctionOnTab(const QString &tab, const QString &name, const QVariantList &args)
{
    return _instance->_runFunctionOnTab(tab, name, args);
}


//----------------------------------------------------------------
// Internal scirpt runners
//----------------------------------------------------------------
std::future<QVariant> JsScriptManager::_runOnAddon(const QString &script)
{
    std::lock_guard<std::mutex> guard(locker);
    scripts[lastId] = std::promise<QVariant>();
    Server::send("runScript", {
        { "id", lastId },
        { "content", script }
    });
    return scripts.at(lastId++).get_future();
}

std::future<QVariant> JsScriptManager::_runFunctionOnAddon(const QString &name, const QVariantList &args)
{
    std::lock_guard<std::mutex> guard(locker);
    scripts[lastId] = std::promise<QVariant>();
    Server::send("runFunction", {
        { "id", lastId },
        { "name", name },
        { "args", args }
    });
    return scripts.at(lastId++).get_future();
}

std::future<QVariant> JsScriptManager::_runOnTab(const QString &tab, const QString &script)
{
    std::lock_guard<std::mutex> guard(locker);
    scripts[lastId] = std::promise<QVariant>();
    Server::send("runPageScript", {
        { "tab", tab},
        { "id", lastId},
        { "content", script}
    });
    return scripts.at(lastId++).get_future();
}

std::future<QVariant> JsScriptManager::_runFunctionOnTab(const QString &tab, const QString &name, const QVariantList &args)
{
    std::lock_guard<std::mutex> guard(locker);
    scripts[lastId] = std::promise<QVariant>();
    Server::send("runPageFunction", {
        { "tab", tab },
        { "id", lastId },
        { "name", name },
        { "args", args }
    });
    return scripts.at(lastId++).get_future();
}

void JsScriptManager::_remove(uint64_t i)
{
    std::lock_guard<std::mutex> guard(locker);
    scripts.erase(i);
}

void JsScriptManager::onJsScriptFinished(QVariantMap args)
{
    uint64_t id = args.value("id").toULongLong();
    auto result = args.value("result");
    if (scripts.count(id) > 0)
    {
        scripts.at(id).set_value(result);
        _remove(id);
    }
    else
    {
        throw "Error : script not exist!";// todo : record error
    }
}

JsScriptManager * JsScriptManager::_instance = nullptr;
